This notebook defines the most focal recurrent copy number units by removing focal changes that are within entire chromosome arm losses and gains. Most focal here meaning:

  • If a chromosome arm is not clearly defined as a gain or loss (and is callable) we look to define the cytoband level status
  • If a cytoband is not clearly defined as a gain or loss (and is callable) we then look to define the gene-level status

Usage

This notebook is intended to be run from the command line with the following (assumes you are in the root directory of the repository):

Rscript -e "rmarkdown::render('analyses/focal-cn-file-preparation/05-define-most-focal-cn-units.Rmd', clean = TRUE)"

Cutoffs:

# The fraction of calls a particular status needs to be
# above to be called the majority status -- the decision
# for a cutoff of 90% here was made to ensure that the status
# is not only the majority status but it is also significantly
# called more than the other status values in the region
status_threshold <- 0.9

# The fraction threshold for determining if enough of a region
# (arm, cytoband, or gene) is callable to determine its status --
# the decision for a cutoff of 50% here was made as it seems reasonable
# to expect a region to be more than 50% callable for a dominant status
# call to be made
uncallable_threshold <- 0.5

Set up

Libraries and functions

library(tidyverse)
── Attaching packages ────────────────────────────────── tidyverse 1.2.1 ──
✔ ggplot2 3.2.0     ✔ purrr   0.3.2
✔ tibble  2.1.3     ✔ dplyr   0.8.3
✔ tidyr   0.8.3     ✔ stringr 1.4.0
✔ readr   1.3.1     ✔ forcats 0.4.0
── Conflicts ───────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()

Files and directories

results_dir <- "results"

Read in files

Read in cytoband status file and format it for what we will need in this notebook.

# Read in the file with consensus CN status data and the UCSC cytoband data --
# generated in `03-add-cytoband-status-consensus.Rmd`
consensus_seg_cytoband_status_df <-
  read_tsv(file.path(results_dir, "consensus_seg_with_ucsc_cytoband_status.tsv.gz")) %>%
  # Need this to not have `chr`
  mutate(
    chr = gsub("chr", "", chr),
    cytoband = paste0(chr, cytoband)
  ) %>%
  select(
    chromosome_arm,
    # Distinguish this dominant status that is based on cytobands, from the status
    dominant_cytoband_status = dominant_status,
    cytoband,
    Kids_First_Biospecimen_ID,
    band_length,
    gain_fraction,
    loss_fraction,
    callable_fraction
  )
Parsed with column specification:
cols(
  Kids_First_Biospecimen_ID = col_character(),
  chr = col_character(),
  cytoband = col_character(),
  dominant_status = col_character(),
  band_length = col_double(),
  callable_fraction = col_double(),
  gain_fraction = col_double(),
  loss_fraction = col_double(),
  chromosome_arm = col_character()
)

Read in the gene-level data.

# Read in the annotated gene level CN file
consensus_seg_autosomes_df <-
  read_tsv(file.path(results_dir, "consensus_seg_annotated_cn_autosomes.tsv.gz")) %>%
  mutate(chromosome_arm = gsub("(p|q).*", "\\1", cytoband))
Parsed with column specification:
cols(
  biospecimen_id = col_character(),
  status = col_character(),
  copy_number = col_double(),
  ploidy = col_double(),
  ensembl = col_character(),
  gene_symbol = col_character(),
  cytoband = col_character(),
  pct_overlap = col_double()
)
# Rename "amplification" status calls to be "gain" for the purpose of this script
consensus_seg_autosomes_df$status <-
  gsub("amplification", "gain", consensus_seg_autosomes_df$status)

Define most focal units

Determine chromosome arm status

consensus_seg_arm_status <- consensus_seg_cytoband_status_df %>%
    # Group by biospecimen ID and region
    group_by(Kids_First_Biospecimen_ID, chromosome_arm) %>%
    # Summarize the weighted means for each status
    summarize(
      loss_fraction_arm = weighted.mean(loss_fraction, band_length),
      gain_fraction_arm = weighted.mean(gain_fraction, band_length),
      callable_fraction_arm = weighted.mean(callable_fraction, band_length)
    ) %>%
    # Define dominant status based the weighted means meeting a status
    # threshold
    mutate(
      dominant_arm_status = case_when(
        callable_fraction_arm < (1 - uncallable_threshold) ~ "uncallable",
        loss_fraction_arm > status_threshold ~ "loss",
        gain_fraction_arm > status_threshold ~ "gain",
        loss_fraction_arm + gain_fraction_arm > status_threshold ~ "unstable",
        TRUE ~ "neutral"
      )
    )

# Display table
consensus_seg_arm_status

Determine cytoband status

We want to include cytoband and gene-level calls for chromosome arms that have not been defined as a gain or loss to make the cytoband-level majority calls.

# Now define the cytoband as that status if more than the `status_threshold` 
# fraction value of the total counts are for that particular status
consensus_seg_cytoband_status <-
  consensus_seg_cytoband_status_df %>%
  mutate(
    dominant_cytoband_status = case_when(
      callable_fraction < (1 - uncallable_threshold) ~ "uncallable",
      loss_fraction > status_threshold ~ "loss",
      gain_fraction > status_threshold ~ "gain",
      loss_fraction + gain_fraction > status_threshold ~ "unstable",
      TRUE ~ "neutral"
    )
  ) 

# Join the consensus seg arm status data and filter to include only neutral
# chromosome arms and non-neutral cytobands
filtered_consensus_cytoband_status <- consensus_seg_cytoband_status %>%
  left_join(
    consensus_seg_arm_status,
    by = c(
      "Kids_First_Biospecimen_ID",
      "chromosome_arm"
    )
  ) %>%
  # Filter the annotated CN data to include only neutral chromosome arms and disagreements
  filter(
    dominant_arm_status %in% c("neutral", "uncallable", "unstable") |
      (
        dominant_cytoband_status != dominant_arm_status &
          # bands that disagree with arm, but are not neutral (or uncallable)
          !(dominant_cytoband_status %in% c("neutral", "uncallable", "unstable"))
      )
  ) %>%
  select(
    Kids_First_Biospecimen_ID,
    cytoband,
    loss_fraction,
    gain_fraction,
    callable_fraction,
    dominant_cytoband_status
  )

# Display table
filtered_consensus_cytoband_status

Determine gene-level status

# Create separate rows for genes span multiple cytobands
# CAUTION: This will require addition of a distinct() statment later to resolve duplicates
# Filtering to only multiband genes first because regexes are slow.
multi_band_genes <- consensus_seg_autosomes_df %>%
  filter(grepl("-", cytoband)) %>%
  extract(cytoband, into = c("chrom", "band"), regex = "([0-9]+)(.+)") %>% # make chrom and band cols
  separate_rows(band, sep = "-") %>% # duplicate rows with more than one band
  unite(cytoband, chrom, band, sep = "") # rejoin chrom and band

# Filter to singleband genes
single_band_genes <- consensus_seg_autosomes_df %>%
  filter(!grepl("-", cytoband))
gene_df <- bind_rows(single_band_genes, multi_band_genes)

# Now create a data.frame with the gene-level status calls for the
# neutral, uncallable, and unstable cytoband-level and chromosome arm-level
# calls
filtered_consensus_gene_status <- gene_df %>%
  left_join(
    consensus_seg_arm_status,
    by = c("biospecimen_id" = "Kids_First_Biospecimen_ID",
           "chromosome_arm")
  ) %>%
  left_join(
    consensus_seg_cytoband_status,
    by = c("biospecimen_id" = "Kids_First_Biospecimen_ID",
           "cytoband")
  ) %>%
  # Filter the annotated CN data to include only neutral arms and cytobands,
  # and disagreements
  filter(
    # Case 1) Gene call disagrees with both arm and cytoband (This captures most
    # we want to keep, including all of when arm and cytoband are neutral, uncallable
    # or unstable, since we have no neutral gene calls in this df):
    (
      status != dominant_arm_status & status != dominant_cytoband_status
    )
    # Case 2) Gene call disagrees with a non-neutral cytoband call.
    # Keep no matter what the arm status
    | (
      status != dominant_cytoband_status
      & dominant_cytoband_status %in% c("gain", "loss")
    )
    # I think that captures everything we want. Cases we don't want include:
    # gene & arm agree, but cytoband is neutral
    # gene & cytoband agree
    # all 3 agree
  ) %>%
  select(Kids_First_Biospecimen_ID = biospecimen_id,
         gene_symbol,
         status) %>%
  # The `distinct()` function is needed to remove duplicates resulting from
  # the band separation into multiple rows step above
  distinct()

# Display table
filtered_consensus_gene_status

Combine arm, cytoband, and gene-level status data

# Rename each the dominant status columns of the data.frames
# to be uniform for the binding rows step and filter out "neutral" calls
consensus_seg_arm_status <- consensus_seg_arm_status %>%
  filter(!(dominant_arm_status == "neutral")) %>%
  rename(dominant_status = dominant_arm_status)

filtered_consensus_cytoband_status <- filtered_consensus_cytoband_status %>%
  filter(!(dominant_cytoband_status == "neutral")) %>%
  rename(dominant_status = dominant_cytoband_status)

# There are no "neutral" calls at the gene level so we do not need to filter
# out those calls here
filtered_consensus_gene_status <- filtered_consensus_gene_status %>%
  rename(dominant_status = status)

# For each of the datasets we're joining, we'll only keep these columns:
cols_to_keep <- c("Kids_First_Biospecimen_ID", "dominant_status")

Combine into one long data frame

# Combine the arm, cytoband, and gene status count data
final_df <- bind_rows(
  arm = select(consensus_seg_arm_status,
               cols_to_keep,
               region = chromosome_arm),
  cytoband = select(filtered_consensus_cytoband_status,
                    cols_to_keep,
                    region = cytoband),
  gene = select(filtered_consensus_gene_status,
                cols_to_keep,
                region = gene_symbol),
  .id = "region_type"
) %>%
  # Reorder columns more sensibly
  select(Kids_First_Biospecimen_ID,
         status = dominant_status,
         region,
         region_type) %>%
  arrange(Kids_First_Biospecimen_ID)

# Print out preview
final_df

Write to a TSV file

# Write final long status table to file
write_tsv(
  final_df,
  file.path(results_dir, "consensus_seg_most_focal_cn_status.tsv.gz")
)

# Display final long status table
final_df %>%
  arrange(region_type)

Session Info

sessionInfo()
R version 3.6.0 (2019-04-26)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Debian GNU/Linux 9 (stretch)

Matrix products: default
BLAS/LAPACK: /usr/lib/libopenblasp-r0.2.19.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=C             
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] forcats_0.4.0   stringr_1.4.0   dplyr_0.8.3     purrr_0.3.2    
[5] readr_1.3.1     tidyr_0.8.3     tibble_2.1.3    ggplot2_3.2.0  
[9] tidyverse_1.2.1

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.1       cellranger_1.1.0 pillar_1.4.2     compiler_3.6.0  
 [5] base64enc_0.1-3  tools_3.6.0      digest_0.6.20    lubridate_1.7.4 
 [9] jsonlite_1.6     evaluate_0.14    nlme_3.1-140     gtable_0.3.0    
[13] lattice_0.20-38  pkgconfig_2.0.2  rlang_0.4.0      cli_1.1.0       
[17] rstudioapi_0.10  yaml_2.2.0       haven_2.1.1      xfun_0.8        
[21] withr_2.1.2      xml2_1.2.0       httr_1.4.0       knitr_1.23      
[25] hms_0.4.2        generics_0.0.2   grid_3.6.0       tidyselect_0.2.5
[29] glue_1.3.1       R6_2.4.0         readxl_1.3.1     rmarkdown_1.13  
[33] modelr_0.1.4     magrittr_1.5     backports_1.1.4  scales_1.0.0    
[37] htmltools_0.3.6  rvest_0.3.4      assertthat_0.2.1 colorspace_1.4-1
[41] stringi_1.4.3    lazyeval_0.2.2   munsell_0.5.0    broom_0.5.2     
[45] crayon_1.3.4    
LS0tCnRpdGxlOiAiRmluZCBtb3N0IGZvY2FsIHJlY3VycmVudCBjb3B5IG51bWJlciB1bml0cyIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKYXV0aG9yOiBDaGFudGUgQmV0aGVsbCBhbmQgQ2FuZGFjZSBTYXZvbmVuIGZvciBBTFNGIENDREwKZGF0ZTogMjAyMAotLS0KClRoaXMgbm90ZWJvb2sgZGVmaW5lcyB0aGUgbW9zdCBmb2NhbCByZWN1cnJlbnQgY29weSBudW1iZXIgdW5pdHMgYnkgcmVtb3ZpbmcgZm9jYWwgY2hhbmdlcyB0aGF0IGFyZSB3aXRoaW4gZW50aXJlIGNocm9tb3NvbWUgYXJtIGxvc3NlcyBhbmQgZ2FpbnMuCl9Nb3N0IGZvY2FsXyBoZXJlIG1lYW5pbmc6CgotIElmIGEgY2hyb21vc29tZSBhcm0gaXMgbm90IGNsZWFybHkgZGVmaW5lZCBhcyBhIGdhaW4gb3IgbG9zcyAoYW5kIGlzIGNhbGxhYmxlKSB3ZSBsb29rIHRvIGRlZmluZSB0aGUgY3l0b2JhbmQgbGV2ZWwgc3RhdHVzCi0gSWYgYSBjeXRvYmFuZCBpcyBub3QgY2xlYXJseSBkZWZpbmVkIGFzIGEgZ2FpbiBvciBsb3NzIChhbmQgaXMgY2FsbGFibGUpIHdlIHRoZW4gbG9vayB0byBkZWZpbmUgdGhlIGdlbmUtbGV2ZWwgc3RhdHVzCgojIyBVc2FnZQoKVGhpcyBub3RlYm9vayBpcyBpbnRlbmRlZCB0byBiZSBydW4gZnJvbSB0aGUgY29tbWFuZCBsaW5lIHdpdGggdGhlIGZvbGxvd2luZyAoYXNzdW1lcyB5b3UgYXJlIGluIHRoZSByb290IGRpcmVjdG9yeSBvZiB0aGUgcmVwb3NpdG9yeSk6CgpgYGAKUnNjcmlwdCAtZSAicm1hcmtkb3duOjpyZW5kZXIoJ2FuYWx5c2VzL2ZvY2FsLWNuLWZpbGUtcHJlcGFyYXRpb24vMDUtZGVmaW5lLW1vc3QtZm9jYWwtY24tdW5pdHMuUm1kJywgY2xlYW4gPSBUUlVFKSIKYGBgCgojIyMgQ3V0b2ZmczogCgpgYGB7cn0KIyBUaGUgZnJhY3Rpb24gb2YgY2FsbHMgYSBwYXJ0aWN1bGFyIHN0YXR1cyBuZWVkcyB0byBiZQojIGFib3ZlIHRvIGJlIGNhbGxlZCB0aGUgbWFqb3JpdHkgc3RhdHVzIC0tIHRoZSBkZWNpc2lvbgojIGZvciBhIGN1dG9mZiBvZiA5MCUgaGVyZSB3YXMgbWFkZSB0byBlbnN1cmUgdGhhdCB0aGUgc3RhdHVzCiMgaXMgbm90IG9ubHkgdGhlIG1ham9yaXR5IHN0YXR1cyBidXQgaXQgaXMgYWxzbyBzaWduaWZpY2FudGx5CiMgY2FsbGVkIG1vcmUgdGhhbiB0aGUgb3RoZXIgc3RhdHVzIHZhbHVlcyBpbiB0aGUgcmVnaW9uCnN0YXR1c190aHJlc2hvbGQgPC0gMC45CgojIFRoZSBmcmFjdGlvbiB0aHJlc2hvbGQgZm9yIGRldGVybWluaW5nIGlmIGVub3VnaCBvZiBhIHJlZ2lvbgojIChhcm0sIGN5dG9iYW5kLCBvciBnZW5lKSBpcyBjYWxsYWJsZSB0byBkZXRlcm1pbmUgaXRzIHN0YXR1cyAtLQojIHRoZSBkZWNpc2lvbiBmb3IgYSBjdXRvZmYgb2YgNTAlIGhlcmUgd2FzIG1hZGUgYXMgaXQgc2VlbXMgcmVhc29uYWJsZQojIHRvIGV4cGVjdCBhIHJlZ2lvbiB0byBiZSBtb3JlIHRoYW4gNTAlIGNhbGxhYmxlIGZvciBhIGRvbWluYW50IHN0YXR1cwojIGNhbGwgdG8gYmUgbWFkZQp1bmNhbGxhYmxlX3RocmVzaG9sZCA8LSAwLjUKYGBgCgojIyBTZXQgdXAKCiMjIyBMaWJyYXJpZXMgYW5kIGZ1bmN0aW9ucwoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMjIyBGaWxlcyBhbmQgZGlyZWN0b3JpZXMKCmBgYHtyfQpyZXN1bHRzX2RpciA8LSAicmVzdWx0cyIKYGBgCgojIyMgUmVhZCBpbiBmaWxlcwoKUmVhZCBpbiBjeXRvYmFuZCBzdGF0dXMgZmlsZSBhbmQgZm9ybWF0IGl0IGZvciB3aGF0IHdlIHdpbGwgbmVlZCBpbiB0aGlzIG5vdGVib29rLiAKCmBgYHtyfQojIFJlYWQgaW4gdGhlIGZpbGUgd2l0aCBjb25zZW5zdXMgQ04gc3RhdHVzIGRhdGEgYW5kIHRoZSBVQ1NDIGN5dG9iYW5kIGRhdGEgLS0KIyBnZW5lcmF0ZWQgaW4gYDAzLWFkZC1jeXRvYmFuZC1zdGF0dXMtY29uc2Vuc3VzLlJtZGAKY29uc2Vuc3VzX3NlZ19jeXRvYmFuZF9zdGF0dXNfZGYgPC0KICByZWFkX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsICJjb25zZW5zdXNfc2VnX3dpdGhfdWNzY19jeXRvYmFuZF9zdGF0dXMudHN2Lmd6IikpICU+JQogICMgTmVlZCB0aGlzIHRvIG5vdCBoYXZlIGBjaHJgCiAgbXV0YXRlKAogICAgY2hyID0gZ3N1YigiY2hyIiwgIiIsIGNociksCiAgICBjeXRvYmFuZCA9IHBhc3RlMChjaHIsIGN5dG9iYW5kKQogICkgJT4lCiAgc2VsZWN0KAogICAgY2hyb21vc29tZV9hcm0sCiAgICAjIERpc3Rpbmd1aXNoIHRoaXMgZG9taW5hbnQgc3RhdHVzIHRoYXQgaXMgYmFzZWQgb24gY3l0b2JhbmRzLCBmcm9tIHRoZSBzdGF0dXMKICAgIGRvbWluYW50X2N5dG9iYW5kX3N0YXR1cyA9IGRvbWluYW50X3N0YXR1cywKICAgIGN5dG9iYW5kLAogICAgS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCwKICAgIGJhbmRfbGVuZ3RoLAogICAgZ2Fpbl9mcmFjdGlvbiwKICAgIGxvc3NfZnJhY3Rpb24sCiAgICBjYWxsYWJsZV9mcmFjdGlvbgogICkKYGBgCgpSZWFkIGluIHRoZSBnZW5lLWxldmVsIGRhdGEuIAoKYGBge3J9CiMgUmVhZCBpbiB0aGUgYW5ub3RhdGVkIGdlbmUgbGV2ZWwgQ04gZmlsZQpjb25zZW5zdXNfc2VnX2F1dG9zb21lc19kZiA8LQogIHJlYWRfdHN2KGZpbGUucGF0aChyZXN1bHRzX2RpciwgImNvbnNlbnN1c19zZWdfYW5ub3RhdGVkX2NuX2F1dG9zb21lcy50c3YuZ3oiKSkgJT4lCiAgbXV0YXRlKGNocm9tb3NvbWVfYXJtID0gZ3N1YigiKHB8cSkuKiIsICJcXDEiLCBjeXRvYmFuZCkpCgojIFJlbmFtZSAiYW1wbGlmaWNhdGlvbiIgc3RhdHVzIGNhbGxzIHRvIGJlICJnYWluIiBmb3IgdGhlIHB1cnBvc2Ugb2YgdGhpcyBzY3JpcHQKY29uc2Vuc3VzX3NlZ19hdXRvc29tZXNfZGYkc3RhdHVzIDwtCiAgZ3N1YigiYW1wbGlmaWNhdGlvbiIsICJnYWluIiwgY29uc2Vuc3VzX3NlZ19hdXRvc29tZXNfZGYkc3RhdHVzKQpgYGAKCiMjIERlZmluZSBtb3N0IGZvY2FsIHVuaXRzCgojIyMgRGV0ZXJtaW5lIGNocm9tb3NvbWUgYXJtIHN0YXR1cwoKYGBge3J9CmNvbnNlbnN1c19zZWdfYXJtX3N0YXR1cyA8LSBjb25zZW5zdXNfc2VnX2N5dG9iYW5kX3N0YXR1c19kZiAlPiUKICAgICMgR3JvdXAgYnkgYmlvc3BlY2ltZW4gSUQgYW5kIHJlZ2lvbgogICAgZ3JvdXBfYnkoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCwgY2hyb21vc29tZV9hcm0pICU+JQogICAgIyBTdW1tYXJpemUgdGhlIHdlaWdodGVkIG1lYW5zIGZvciBlYWNoIHN0YXR1cwogICAgc3VtbWFyaXplKAogICAgICBsb3NzX2ZyYWN0aW9uX2FybSA9IHdlaWdodGVkLm1lYW4obG9zc19mcmFjdGlvbiwgYmFuZF9sZW5ndGgpLAogICAgICBnYWluX2ZyYWN0aW9uX2FybSA9IHdlaWdodGVkLm1lYW4oZ2Fpbl9mcmFjdGlvbiwgYmFuZF9sZW5ndGgpLAogICAgICBjYWxsYWJsZV9mcmFjdGlvbl9hcm0gPSB3ZWlnaHRlZC5tZWFuKGNhbGxhYmxlX2ZyYWN0aW9uLCBiYW5kX2xlbmd0aCkKICAgICkgJT4lCiAgICAjIERlZmluZSBkb21pbmFudCBzdGF0dXMgYmFzZWQgdGhlIHdlaWdodGVkIG1lYW5zIG1lZXRpbmcgYSBzdGF0dXMKICAgICMgdGhyZXNob2xkCiAgICBtdXRhdGUoCiAgICAgIGRvbWluYW50X2FybV9zdGF0dXMgPSBjYXNlX3doZW4oCiAgICAgICAgY2FsbGFibGVfZnJhY3Rpb25fYXJtIDwgKDEgLSB1bmNhbGxhYmxlX3RocmVzaG9sZCkgfiAidW5jYWxsYWJsZSIsCiAgICAgICAgbG9zc19mcmFjdGlvbl9hcm0gPiBzdGF0dXNfdGhyZXNob2xkIH4gImxvc3MiLAogICAgICAgIGdhaW5fZnJhY3Rpb25fYXJtID4gc3RhdHVzX3RocmVzaG9sZCB+ICJnYWluIiwKICAgICAgICBsb3NzX2ZyYWN0aW9uX2FybSArIGdhaW5fZnJhY3Rpb25fYXJtID4gc3RhdHVzX3RocmVzaG9sZCB+ICJ1bnN0YWJsZSIsCiAgICAgICAgVFJVRSB+ICJuZXV0cmFsIgogICAgICApCiAgICApCgojIERpc3BsYXkgdGFibGUKY29uc2Vuc3VzX3NlZ19hcm1fc3RhdHVzCmBgYAoKIyMjIERldGVybWluZSBjeXRvYmFuZCBzdGF0dXMKCldlIHdhbnQgdG8gaW5jbHVkZSBjeXRvYmFuZCBhbmQgZ2VuZS1sZXZlbCBjYWxscyBmb3IgY2hyb21vc29tZSBhcm1zIHRoYXQgaGF2ZSBub3QgYmVlbiBkZWZpbmVkIGFzIGEgZ2FpbiBvciBsb3NzIHRvIG1ha2UgdGhlIGN5dG9iYW5kLWxldmVsIG1ham9yaXR5IGNhbGxzLgoKYGBge3J9CiMgTm93IGRlZmluZSB0aGUgY3l0b2JhbmQgYXMgdGhhdCBzdGF0dXMgaWYgbW9yZSB0aGFuIHRoZSBgc3RhdHVzX3RocmVzaG9sZGAgCiMgZnJhY3Rpb24gdmFsdWUgb2YgdGhlIHRvdGFsIGNvdW50cyBhcmUgZm9yIHRoYXQgcGFydGljdWxhciBzdGF0dXMKY29uc2Vuc3VzX3NlZ19jeXRvYmFuZF9zdGF0dXMgPC0KICBjb25zZW5zdXNfc2VnX2N5dG9iYW5kX3N0YXR1c19kZiAlPiUKICBtdXRhdGUoCiAgICBkb21pbmFudF9jeXRvYmFuZF9zdGF0dXMgPSBjYXNlX3doZW4oCiAgICAgIGNhbGxhYmxlX2ZyYWN0aW9uIDwgKDEgLSB1bmNhbGxhYmxlX3RocmVzaG9sZCkgfiAidW5jYWxsYWJsZSIsCiAgICAgIGxvc3NfZnJhY3Rpb24gPiBzdGF0dXNfdGhyZXNob2xkIH4gImxvc3MiLAogICAgICBnYWluX2ZyYWN0aW9uID4gc3RhdHVzX3RocmVzaG9sZCB+ICJnYWluIiwKICAgICAgbG9zc19mcmFjdGlvbiArIGdhaW5fZnJhY3Rpb24gPiBzdGF0dXNfdGhyZXNob2xkIH4gInVuc3RhYmxlIiwKICAgICAgVFJVRSB+ICJuZXV0cmFsIgogICAgKQogICkgCgojIEpvaW4gdGhlIGNvbnNlbnN1cyBzZWcgYXJtIHN0YXR1cyBkYXRhIGFuZCBmaWx0ZXIgdG8gaW5jbHVkZSBvbmx5IG5ldXRyYWwKIyBjaHJvbW9zb21lIGFybXMgYW5kIG5vbi1uZXV0cmFsIGN5dG9iYW5kcwpmaWx0ZXJlZF9jb25zZW5zdXNfY3l0b2JhbmRfc3RhdHVzIDwtIGNvbnNlbnN1c19zZWdfY3l0b2JhbmRfc3RhdHVzICU+JQogIGxlZnRfam9pbigKICAgIGNvbnNlbnN1c19zZWdfYXJtX3N0YXR1cywKICAgIGJ5ID0gYygKICAgICAgIktpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQiLAogICAgICAiY2hyb21vc29tZV9hcm0iCiAgICApCiAgKSAlPiUKICAjIEZpbHRlciB0aGUgYW5ub3RhdGVkIENOIGRhdGEgdG8gaW5jbHVkZSBvbmx5IG5ldXRyYWwgY2hyb21vc29tZSBhcm1zIGFuZCBkaXNhZ3JlZW1lbnRzCiAgZmlsdGVyKAogICAgZG9taW5hbnRfYXJtX3N0YXR1cyAlaW4lIGMoIm5ldXRyYWwiLCAidW5jYWxsYWJsZSIsICJ1bnN0YWJsZSIpIHwKICAgICAgKAogICAgICAgIGRvbWluYW50X2N5dG9iYW5kX3N0YXR1cyAhPSBkb21pbmFudF9hcm1fc3RhdHVzICYKICAgICAgICAgICMgYmFuZHMgdGhhdCBkaXNhZ3JlZSB3aXRoIGFybSwgYnV0IGFyZSBub3QgbmV1dHJhbCAob3IgdW5jYWxsYWJsZSkKICAgICAgICAgICEoZG9taW5hbnRfY3l0b2JhbmRfc3RhdHVzICVpbiUgYygibmV1dHJhbCIsICJ1bmNhbGxhYmxlIiwgInVuc3RhYmxlIikpCiAgICAgICkKICApICU+JQogIHNlbGVjdCgKICAgIEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQsCiAgICBjeXRvYmFuZCwKICAgIGxvc3NfZnJhY3Rpb24sCiAgICBnYWluX2ZyYWN0aW9uLAogICAgY2FsbGFibGVfZnJhY3Rpb24sCiAgICBkb21pbmFudF9jeXRvYmFuZF9zdGF0dXMKICApCgojIERpc3BsYXkgdGFibGUKZmlsdGVyZWRfY29uc2Vuc3VzX2N5dG9iYW5kX3N0YXR1cwpgYGAKCiMjIyBEZXRlcm1pbmUgZ2VuZS1sZXZlbCBzdGF0dXMKCmBgYHtyfQojIENyZWF0ZSBzZXBhcmF0ZSByb3dzIGZvciBnZW5lcyBzcGFuIG11bHRpcGxlIGN5dG9iYW5kcwojIENBVVRJT046IFRoaXMgd2lsbCByZXF1aXJlIGFkZGl0aW9uIG9mIGEgZGlzdGluY3QoKSBzdGF0bWVudCBsYXRlciB0byByZXNvbHZlIGR1cGxpY2F0ZXMKIyBGaWx0ZXJpbmcgdG8gb25seSBtdWx0aWJhbmQgZ2VuZXMgZmlyc3QgYmVjYXVzZSByZWdleGVzIGFyZSBzbG93LgptdWx0aV9iYW5kX2dlbmVzIDwtIGNvbnNlbnN1c19zZWdfYXV0b3NvbWVzX2RmICU+JQogIGZpbHRlcihncmVwbCgiLSIsIGN5dG9iYW5kKSkgJT4lCiAgZXh0cmFjdChjeXRvYmFuZCwgaW50byA9IGMoImNocm9tIiwgImJhbmQiKSwgcmVnZXggPSAiKFswLTldKykoLispIikgJT4lICMgbWFrZSBjaHJvbSBhbmQgYmFuZCBjb2xzCiAgc2VwYXJhdGVfcm93cyhiYW5kLCBzZXAgPSAiLSIpICU+JSAjIGR1cGxpY2F0ZSByb3dzIHdpdGggbW9yZSB0aGFuIG9uZSBiYW5kCiAgdW5pdGUoY3l0b2JhbmQsIGNocm9tLCBiYW5kLCBzZXAgPSAiIikgIyByZWpvaW4gY2hyb20gYW5kIGJhbmQKCiMgRmlsdGVyIHRvIHNpbmdsZWJhbmQgZ2VuZXMKc2luZ2xlX2JhbmRfZ2VuZXMgPC0gY29uc2Vuc3VzX3NlZ19hdXRvc29tZXNfZGYgJT4lCiAgZmlsdGVyKCFncmVwbCgiLSIsIGN5dG9iYW5kKSkKZ2VuZV9kZiA8LSBiaW5kX3Jvd3Moc2luZ2xlX2JhbmRfZ2VuZXMsIG11bHRpX2JhbmRfZ2VuZXMpCgojIE5vdyBjcmVhdGUgYSBkYXRhLmZyYW1lIHdpdGggdGhlIGdlbmUtbGV2ZWwgc3RhdHVzIGNhbGxzIGZvciB0aGUKIyBuZXV0cmFsLCB1bmNhbGxhYmxlLCBhbmQgdW5zdGFibGUgY3l0b2JhbmQtbGV2ZWwgYW5kIGNocm9tb3NvbWUgYXJtLWxldmVsCiMgY2FsbHMKZmlsdGVyZWRfY29uc2Vuc3VzX2dlbmVfc3RhdHVzIDwtIGdlbmVfZGYgJT4lCiAgbGVmdF9qb2luKAogICAgY29uc2Vuc3VzX3NlZ19hcm1fc3RhdHVzLAogICAgYnkgPSBjKCJiaW9zcGVjaW1lbl9pZCIgPSAiS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCIsCiAgICAgICAgICAgImNocm9tb3NvbWVfYXJtIikKICApICU+JQogIGxlZnRfam9pbigKICAgIGNvbnNlbnN1c19zZWdfY3l0b2JhbmRfc3RhdHVzLAogICAgYnkgPSBjKCJiaW9zcGVjaW1lbl9pZCIgPSAiS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCIsCiAgICAgICAgICAgImN5dG9iYW5kIikKICApICU+JQogICMgRmlsdGVyIHRoZSBhbm5vdGF0ZWQgQ04gZGF0YSB0byBpbmNsdWRlIG9ubHkgbmV1dHJhbCBhcm1zIGFuZCBjeXRvYmFuZHMsCiAgIyBhbmQgZGlzYWdyZWVtZW50cwogIGZpbHRlcigKICAgICMgQ2FzZSAxKSBHZW5lIGNhbGwgZGlzYWdyZWVzIHdpdGggYm90aCBhcm0gYW5kIGN5dG9iYW5kIChUaGlzIGNhcHR1cmVzIG1vc3QKICAgICMgd2Ugd2FudCB0byBrZWVwLCBpbmNsdWRpbmcgYWxsIG9mIHdoZW4gYXJtIGFuZCBjeXRvYmFuZCBhcmUgbmV1dHJhbCwgdW5jYWxsYWJsZQogICAgIyBvciB1bnN0YWJsZSwgc2luY2Ugd2UgaGF2ZSBubyBuZXV0cmFsIGdlbmUgY2FsbHMgaW4gdGhpcyBkZik6CiAgICAoCiAgICAgIHN0YXR1cyAhPSBkb21pbmFudF9hcm1fc3RhdHVzICYgc3RhdHVzICE9IGRvbWluYW50X2N5dG9iYW5kX3N0YXR1cwogICAgKQogICAgIyBDYXNlIDIpIEdlbmUgY2FsbCBkaXNhZ3JlZXMgd2l0aCBhIG5vbi1uZXV0cmFsIGN5dG9iYW5kIGNhbGwuCiAgICAjIEtlZXAgbm8gbWF0dGVyIHdoYXQgdGhlIGFybSBzdGF0dXMKICAgIHwgKAogICAgICBzdGF0dXMgIT0gZG9taW5hbnRfY3l0b2JhbmRfc3RhdHVzCiAgICAgICYgZG9taW5hbnRfY3l0b2JhbmRfc3RhdHVzICVpbiUgYygiZ2FpbiIsICJsb3NzIikKICAgICkKICAgICMgSSB0aGluayB0aGF0IGNhcHR1cmVzIGV2ZXJ5dGhpbmcgd2Ugd2FudC4gQ2FzZXMgd2UgZG9uJ3Qgd2FudCBpbmNsdWRlOgogICAgIyBnZW5lICYgYXJtIGFncmVlLCBidXQgY3l0b2JhbmQgaXMgbmV1dHJhbAogICAgIyBnZW5lICYgY3l0b2JhbmQgYWdyZWUKICAgICMgYWxsIDMgYWdyZWUKICApICU+JQogIHNlbGVjdChLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEID0gYmlvc3BlY2ltZW5faWQsCiAgICAgICAgIGdlbmVfc3ltYm9sLAogICAgICAgICBzdGF0dXMpICU+JQogICMgVGhlIGBkaXN0aW5jdCgpYCBmdW5jdGlvbiBpcyBuZWVkZWQgdG8gcmVtb3ZlIGR1cGxpY2F0ZXMgcmVzdWx0aW5nIGZyb20KICAjIHRoZSBiYW5kIHNlcGFyYXRpb24gaW50byBtdWx0aXBsZSByb3dzIHN0ZXAgYWJvdmUKICBkaXN0aW5jdCgpCgojIERpc3BsYXkgdGFibGUKZmlsdGVyZWRfY29uc2Vuc3VzX2dlbmVfc3RhdHVzCmBgYAoKIyMgQ29tYmluZSBhcm0sIGN5dG9iYW5kLCBhbmQgZ2VuZS1sZXZlbCBzdGF0dXMgZGF0YQoKYGBge3J9CiMgUmVuYW1lIGVhY2ggdGhlIGRvbWluYW50IHN0YXR1cyBjb2x1bW5zIG9mIHRoZSBkYXRhLmZyYW1lcwojIHRvIGJlIHVuaWZvcm0gZm9yIHRoZSBiaW5kaW5nIHJvd3Mgc3RlcCBhbmQgZmlsdGVyIG91dCAibmV1dHJhbCIgY2FsbHMKY29uc2Vuc3VzX3NlZ19hcm1fc3RhdHVzIDwtIGNvbnNlbnN1c19zZWdfYXJtX3N0YXR1cyAlPiUKICBmaWx0ZXIoIShkb21pbmFudF9hcm1fc3RhdHVzID09ICJuZXV0cmFsIikpICU+JQogIHJlbmFtZShkb21pbmFudF9zdGF0dXMgPSBkb21pbmFudF9hcm1fc3RhdHVzKQoKZmlsdGVyZWRfY29uc2Vuc3VzX2N5dG9iYW5kX3N0YXR1cyA8LSBmaWx0ZXJlZF9jb25zZW5zdXNfY3l0b2JhbmRfc3RhdHVzICU+JQogIGZpbHRlcighKGRvbWluYW50X2N5dG9iYW5kX3N0YXR1cyA9PSAibmV1dHJhbCIpKSAlPiUKICByZW5hbWUoZG9taW5hbnRfc3RhdHVzID0gZG9taW5hbnRfY3l0b2JhbmRfc3RhdHVzKQoKIyBUaGVyZSBhcmUgbm8gIm5ldXRyYWwiIGNhbGxzIGF0IHRoZSBnZW5lIGxldmVsIHNvIHdlIGRvIG5vdCBuZWVkIHRvIGZpbHRlcgojIG91dCB0aG9zZSBjYWxscyBoZXJlCmZpbHRlcmVkX2NvbnNlbnN1c19nZW5lX3N0YXR1cyA8LSBmaWx0ZXJlZF9jb25zZW5zdXNfZ2VuZV9zdGF0dXMgJT4lCiAgcmVuYW1lKGRvbWluYW50X3N0YXR1cyA9IHN0YXR1cykKCiMgRm9yIGVhY2ggb2YgdGhlIGRhdGFzZXRzIHdlJ3JlIGpvaW5pbmcsIHdlJ2xsIG9ubHkga2VlcCB0aGVzZSBjb2x1bW5zOgpjb2xzX3RvX2tlZXAgPC0gYygiS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCIsICJkb21pbmFudF9zdGF0dXMiKQpgYGAKCkNvbWJpbmUgaW50byBvbmUgbG9uZyBkYXRhIGZyYW1lCgpgYGB7cn0KIyBDb21iaW5lIHRoZSBhcm0sIGN5dG9iYW5kLCBhbmQgZ2VuZSBzdGF0dXMgY291bnQgZGF0YQpmaW5hbF9kZiA8LSBiaW5kX3Jvd3MoCiAgYXJtID0gc2VsZWN0KGNvbnNlbnN1c19zZWdfYXJtX3N0YXR1cywKICAgICAgICAgICAgICAgY29sc190b19rZWVwLAogICAgICAgICAgICAgICByZWdpb24gPSBjaHJvbW9zb21lX2FybSksCiAgY3l0b2JhbmQgPSBzZWxlY3QoZmlsdGVyZWRfY29uc2Vuc3VzX2N5dG9iYW5kX3N0YXR1cywKICAgICAgICAgICAgICAgICAgICBjb2xzX3RvX2tlZXAsCiAgICAgICAgICAgICAgICAgICAgcmVnaW9uID0gY3l0b2JhbmQpLAogIGdlbmUgPSBzZWxlY3QoZmlsdGVyZWRfY29uc2Vuc3VzX2dlbmVfc3RhdHVzLAogICAgICAgICAgICAgICAgY29sc190b19rZWVwLAogICAgICAgICAgICAgICAgcmVnaW9uID0gZ2VuZV9zeW1ib2wpLAogIC5pZCA9ICJyZWdpb25fdHlwZSIKKSAlPiUKICAjIFJlb3JkZXIgY29sdW1ucyBtb3JlIHNlbnNpYmx5CiAgc2VsZWN0KEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQsCiAgICAgICAgIHN0YXR1cyA9IGRvbWluYW50X3N0YXR1cywKICAgICAgICAgcmVnaW9uLAogICAgICAgICByZWdpb25fdHlwZSkgJT4lCiAgYXJyYW5nZShLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEKQoKIyBQcmludCBvdXQgcHJldmlldwpmaW5hbF9kZgpgYGAKCiMjIFdyaXRlIHRvIGEgVFNWIGZpbGUgCgpgYGB7cn0KIyBXcml0ZSBmaW5hbCBsb25nIHN0YXR1cyB0YWJsZSB0byBmaWxlCndyaXRlX3RzdigKICBmaW5hbF9kZiwKICBmaWxlLnBhdGgocmVzdWx0c19kaXIsICJjb25zZW5zdXNfc2VnX21vc3RfZm9jYWxfY25fc3RhdHVzLnRzdi5neiIpCikKCiMgRGlzcGxheSBmaW5hbCBsb25nIHN0YXR1cyB0YWJsZQpmaW5hbF9kZiAlPiUKICBhcnJhbmdlKHJlZ2lvbl90eXBlKQpgYGAKCiMjIFNlc3Npb24gSW5mbwoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCg==